/**********************************************************************
 * File:        arab_classes.cpp
 * Description: This file groups all the classes inhereted from Tesseract's classes that
 *				need more attributes to be able to store Arabic text features and properties.
 * Author:      Fuad Jamour
 * Created:     Fri Mar 13 2009
 **********************************************************************/

#ifndef		IMG_MACROS_H
#define		IMG_MACROS_H
#include	"img.h"
#include	"points.h"
#include	"arab_classes.h"
#include	"elst.h"
#include	"elst2.h"
#include	<string>
#include "globals.h"
#include <cstdlib>





#define		D00		0.000000
#define		D45		0.785398
#define		D90		1.570796
#define		D135	2.356194
#define		D180	3.141592
#define		D225	3.926990
#define		D270	4.712388
#define		D315	5.497787
#define		D360	6.283185



enum feature {BLACK_PT, END_PT, BRANCH_PT, CROSS_PT, ASCENDER_PT, DESCENDER_PT, VISITED_PT, NOT_FEATURE_PT, EDGE_PT, CUT_PT, BAD_BRANCH_PT, SUBTLE_BRANCH_PT};


/**********************************************************************	
*				5	6	7
*				4	P	0
*				3	2	1	
* The N8 sequence above is used throughout the code
************************************************************************/
inline void getN8(const ICOORD coords, BYTE N8[], IMAGE* img) {		//Macro to get the 8 neighbors of a pixel
	N8[0] = img->pixel(coords.x() + 1, coords.y());
	N8[1] = img->pixel(coords.x() + 1, coords.y() - 1);
	N8[2] = img->pixel(coords.x(), coords.y() - 1);
	N8[3] = img->pixel(coords.x() - 1 , coords.y() - 1);
	N8[4] = img->pixel(coords.x() - 1, coords.y());
	N8[5] = img->pixel(coords.x() - 1, coords.y() + 1);
	N8[6] = img->pixel(coords.x(), coords.y() + 1);
	N8[7] = img->pixel(coords.x() + 1, coords.y() + 1);
}

inline BYTE findSimpleFeature(BYTE N8[], BYTE thinnedGreyLevel) {	//Macro to find Branch, Cross, and End points
	inT8	NZ = 0;													
	inT8	NT = 0;										
	BYTE	PrevPoint = N8[7];										

	for (int i = 0; i < 8; i++)
		if (N8[i] == thinnedGreyLevel)
			NZ++;

	for (int i = 0; i < 8; i++) {
		if (PrevPoint == thinnedGreyLevel && N8[i] != thinnedGreyLevel)
			NT++;
		PrevPoint = N8[i];
	}

	if (NZ == 1)
		return	END_PT;
	if (NT == 3)
		return	BRANCH_PT;
	if (NT == 4)
		return	CROSS_PT;
	return NOT_FEATURE_PT;
}

inline void getDirCoords(const int dir, ICOORD &coords) {		
	if(dir == -1) return;

	switch(dir) {
		case 0:
			coords.set_x(coords.x() + 1); break;
		case 1:
			coords.set_x(coords.x() + 1); coords.set_y(coords.y() - 1); break;
		case 2:
			coords.set_y(coords.y() - 1); break;
		case 3:
			coords.set_x(coords.x() - 1); coords.set_y(coords.y() - 1); break;
		case 4:
			coords.set_x(coords.x() - 1); break;
		case 5:
			coords.set_x(coords.x() - 1); coords.set_y(coords.y() + 1); break;
		case 6:
			coords.set_y(coords.y() + 1); break;
		case 7:
			coords.set_x(coords.x() + 1); coords.set_y(coords.y() + 1); break;
		}
}

inline bool N8GotSimpleFeature(BYTE N8[]) {
	for(int i = 0; i < 8; i++) {
		if(N8[i] == BRANCH_PT
		||N8[i] == CROSS_PT
		||N8[i] == END_PT)
		return true;
	}
	return false;
}

inline double findDistLinePt(ICOORD L1, ICOORD L2, ICOORD PT) {
	if (L1 == L2 || PT == L1 || PT == L2)
		return 0;
	if(L2 == ICOORD(0, 0))
		return 0;

	return 
		abs((L2.x() - L1.x()) * (L1.y() - PT.y()) - (L1.x() - PT.x()) * (L2.y() - L1.y())) / 
		sqrt((double)((L2.x() - L1.x()) * (L2.x() - L1.x()) + (L2.y() - L1.y()) * (L2.y() - L1.y())));
}

inline int absFJ(ICOORD disp) {
	return max(abs(disp.x()), abs(disp.y()));
}

inline bool isAnglesEqual(double angle, double base, double tol) {	//returns true if angle is within base+-tol
	if(angle > D360) {
		angle = angle*D180/180;
	}
	if(base > D360) {
		base = base*D180/180;
	}
	if(tol > D360) {
		tol = tol*D180/180;
	}
	if(base == 0 && (angle < D90 || angle > D270)) {				//REVISE to cover all possible cases
		base = D180;												//all angles are between [0, 2*pi]
		if(angle < D90)
			angle = angle + D90;
		else 
			if(angle > D270)
				angle = angle - D90;
	}
	return ((angle >= base - tol) && (angle <= base + tol));
}


inline void testFilter (               //Conv Function
uinT8 ** pixels,                 //Of window
uinT8 bytespp,                   //1 or 3 for colour
inT32 win_wd,                    //Window width
inT32 win_ht,                    //Window height
uinT8 ret_white_value,           //White value to RETURN
uinT8 * result)  {

	*result = pixels[0][0];
	for(int xt = 0; xt < win_wd; xt++) {
		for(int yt = 0; yt < win_ht; yt++) {
			*result = min(pixels[xt][yt], *result) ;
		}
	}
	//*result /= (win_wd * win_ht);
}

inline void fillSmallHoles (               //Conv Function
uinT8 ** pixels,                 //Of window
uinT8 bytespp,                   //1 or 3 for colour
inT32 win_wd,                    //Window width
inT32 win_ht,                    //Window height
uinT8 ret_white_value,           //White value to RETURN
uinT8 * result)  {

	bool fill = true;

	if(pixels[1][1] == 255) {
		for(int i = 0; i < win_wd; i++) {
			for(int j = 0; j < win_wd; j++) {
				if(i != 1 && j != 1) {
					if(pixels[i][j] == 255) {
						fill = false;
						break;
					}
				}
			}
			if(!fill) {
				break;
			}
		}
	}
	if(fill)
		*result = 0;
	else 
		*result = 255;
}

inline void morphologicalErosion (               //Conv Function
uinT8 ** pixels,                 //Of window
uinT8 bytespp,                   //1 or 3 for colour
inT32 win_wd,                    //Window width
inT32 win_ht,                    //Window height
uinT8 ret_white_value,           //White value to RETURN
uinT8 * result)  {

	int	size = win_wd;
	uinT8**	elementArray = new uinT8*[size];
	for(int i =0; i < size; i++) {
		elementArray[i] = new uinT8[size];
	}

	/*for(int yt = 0; yt < size; yt++) {
		for(int xt = 0; xt < 4; xt++) {
			if(abs((xt-size/2)*(xt-size/2) + (yt-size/2)*(yt-size/2)) <= (size*size/4)) {
			elementArray[yt][xt] = 0;
			} else {
				elementArray[yt][xt] = 255;
			}
		}
	}*/
	for(int yt = 0; yt < size; yt++) {
		for(int xt = 0; xt < size; xt++) {
			elementArray[yt][xt] = 255;
		}
	}
	for(int yt = 0; yt < size; yt++) {
		elementArray[yt][size/2] = 0;
	}
	for(int xt = 0; xt < size; xt++) {
		elementArray[size/2][xt] = 0;
	}
	
	bool flag = true;
	
	for(int yt = 0; yt < size; yt++) {
		for(int xt = 0; xt < size; xt++) {
			if(elementArray[yt][xt] == 0 && pixels[yt][xt] == 255) {
				flag = false;
			}
		}
	}

	if(flag) {
		*result = 0;
	} else {
		*result = 255;
	}

	for(int i = 0; i < size; i++) {
		delete[] elementArray[i];
	}
}

inline void morphologicalDilation (               //Conv Function
uinT8 ** pixels,                 //Of window
uinT8 bytespp,                   //1 or 3 for colour
inT32 win_wd,                    //Window width
inT32 win_ht,                    //Window height
uinT8 ret_white_value,           //White value to RETURN
uinT8 * result)  {

	int	size = win_wd;
	uinT8**	elementArray = new uinT8*[size];
	for(int i =0; i < size; i++) {
		elementArray[i] = new uinT8[size];
	}

	/*for(int yt = 0; yt < size; yt++) {
		for(int xt = 0; xt < size; xt++) {
			if(abs((xt-size/2)*(xt-size/2) + (yt-size/2)*(yt-size/2)) <= (size*size/4)) {
			elementArray[yt][xt] = 0;
			} else {
				elementArray[yt][xt] = 255;
			}
		}
	}*/

	for(int yt = 0; yt < size; yt++) {
		for(int xt = 0; xt < size; xt++) {
			elementArray[yt][xt] = 255;
		}
	}
	for(int yt = 0; yt < size; yt++) {
		elementArray[yt][size/2] = 0;
	}
	for(int xt = 0; xt < size; xt++) {
		elementArray[size/2][xt] = 0;
	}
	
	bool flag = false;
	
	for(int yt = 0; yt < size; yt++) {
		for(int xt = 0; xt < size; xt++) {
			if(elementArray[yt][xt] == 0 && pixels[yt][xt] == 0) {
				flag = true;
			}
		}
	}

	if(flag) {
		*result = 0;
	} else {
		*result = 255;
	}

	for(int i = 0; i < size; i++) {
		delete[] elementArray[i];
	}
}

inline int featComparator(                     //sort features
                    const void *feat1p,  //ptr to ptr to feat1
                    const void *feat2p   //ptr to ptr to feat2
                   ) {
  PixelWithFeatures *
    feat1 = *(PixelWithFeatures **) feat1p;
  PixelWithFeatures *
    feat2 = *(PixelWithFeatures **) feat2p;

  return feat1->coords.x() - feat2->coords.x();
}

inline int routeComparator(                     //sort features
                    const void *route1p,  //ptr to ptr to feat1
                    const void *route2p   //ptr to ptr to feat2
                   ) {
  Route *
    route1 = *(Route **) route1p;
  Route *
    route2 = *(Route **) route2p;

	//PixelWithFeatures PT1_route1 = route1->firstPt.coords.x() < route1->lastPt.coords.x() ? route1->firstPt : route1->lastPt;//left
	PixelWithFeatures PT2_route1 = route1->firstPt.coords.x() <= route1->lastPt.coords.x() ? route1->firstPt : route1->lastPt;//right

	//PixelWithFeatures PT1_route2 = route2->firstPt.coords.x() < route2->lastPt.coords.x() ? route2->firstPt : route2->lastPt;//left
	PixelWithFeatures PT2_route2 = route2->firstPt.coords.x() <= route2->lastPt.coords.x() ? route2->firstPt : route2->lastPt;//right

	return PT2_route1.coords.x() - PT2_route2.coords.x();
}

inline int routeLengthComparator(                     //sort features
                    const void *route1p,  //ptr to ptr to feat1
                    const void *route2p   //ptr to ptr to feat2
                   ) {
  Route *
    route1 = *(Route **) route1p;
  Route *
    route2 = *(Route **) route2p;

	int length1 = route1->coordsList.length();

	int length2 = route2->coordsList.length();

	return length1 - length2;
}

inline int segPointsComparator(                     //sort features
                    const void *seg1p,  //ptr to ptr to feat1
                    const void *seg2p   //ptr to ptr to feat2
                   ) {
  SEG_POINT *
    seg1 = *(SEG_POINT **) seg1p;
  SEG_POINT *
    seg2 = *(SEG_POINT **) seg2p;

  return seg1->x() - seg2->x();
}

inline	STRING readTifText() {
	char	ch1, ch2, ch3;
	STRING	str = "";
	STRING	fileName = imagefile;
	fileName += ".tif";
	FILE* tifFP = fopen(fileName.string(), "r");
	while((ch1 = (char)getc(tifFP)) != 'A'
		| (ch2 = (char)getc(tifFP)) != 'W'
		| (ch3 = (char)getc(tifFP)) != '2') 
	{
		ungetc(ch3, tifFP);
		ungetc(ch2, tifFP);
	}
	getc(tifFP);

	while((ch1 = getc(tifFP)) != ';') {
		str += ch1;
	}
	fclose(tifFP);

	return str;
}

#endif